import { lastValueFrom, Observable } from "rxjs";
import { ClientProxy, ClientProxyFactory, Transport, Closeable } from "@nestjs/microservices";

import { ENUM_APP } from "qqlx-core";
import { getAppTrans } from "qqlx-cdk";

import { BusRpc } from "./app.droplet-bus";
import { Logger } from "../lib/log";

export class RpcConnection {
  private BUS_RPC!: BusRpc;
  private BUS_RPC_CLIENT_ID!: string;

  constructor(BusRpc: BusRpc, app: ENUM_APP) {
    this.BUS_RPC = BusRpc;
    this.BUS_RPC_CLIENT_ID = BusRpc.getClientId();
    this.initial(app);
  }

  private REMOTE_APP!: ENUM_APP;
  private REMOTE_APP_NAME!: string;
  private async initial(remote_app: ENUM_APP) {
    this.REMOTE_APP = remote_app;
    this.REMOTE_APP_NAME = getAppTrans(this.REMOTE_APP)!.zh || "UNKNOWN";

    try {
      await this.doRemoteAppServiceConnection(remote_app);
      this.keepRemoteAppServiceConnection();
    } catch (error) {
      Logger.info({
        log: `🚚/❌无法进行TCP链接：${this.REMOTE_APP_NAME}（${this.BUS_RPC_CLIENT_ID}）程序即将退出`,
        detail: error as any,
      });
      process.exit();
    }
  }

  onModuleDestroy() {
    this.clearDotTimer();
    this.CLIENT?.close();
  }

  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 保持 TCP 链接活跃

  /** 保持 TCP 链接活跃 */
  private DOT_TIMER: NodeJS.Timeout | null = null;
  keepRemoteAppServiceConnection() {
    this.clearDotTimer();
    this.DOT_TIMER = setInterval(async () => {
      try {
        const ob: Observable<any> = this.CLIENT.send(`/system/dot`, { app: this.REMOTE_APP });
        await lastValueFrom<any>(ob);

        Logger.trace({ log: `🚚正在保持TCP链接活跃：${this.CLIENT_NAME}` });
      } catch (error) {
        Logger.trace({ log: `🚚/❌TCP链接异常：${this.CLIENT_NAME}`, detail: error as any });
        this.clearDotTimer();
        this.restart();
      }
    }, 1000 * 10);
  }

  private clearDotTimer() {
    if (this.DOT_TIMER) {
      clearInterval(this.DOT_TIMER);
      this.DOT_TIMER = null;
    }
  }

  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 创建TCP链接

  /** 重新创建TCP链接（会连续尝试24小时/5s）*/
  private async restart() {
    const restart_gap = 10000;
    const restart_max = (1000 * 60 * 60 * 24) / restart_gap;
    const count = { value: 0 };
    const RESTART_TIMER = setInterval(async () => {
      try {
        await this.doRemoteAppServiceConnection(this.REMOTE_APP);
        clearInterval(RESTART_TIMER); // 一旦成功就不再轮询
        this.keepRemoteAppServiceConnection(); // 重新保持TCP链接活跃
      } catch {
        count.value++;
        Logger.trace({ log: `🚚/❌无法重新创建TCP客户端：${this.CLIENT_NAME} 第${count.value}次` });
      } finally {
        // 连续尝试24小时
        if (restart_max <= count.value) clearInterval(RESTART_TIMER);
      }
    }, restart_gap);
  }

  private REMOTE_IP!: string;
  private REMOTE_PORT!: number;

  public CLIENT!: ClientProxy & Closeable;
  private CLIENT_NAME = "";

  /** 创建 TCP 链接 */
  private async doRemoteAppServiceConnection(remote_app: ENUM_APP) {
    this.CLIENT_NAME = `Local ${getAppTrans(remote_app)!.zh} RPC Connection（${this.BUS_RPC_CLIENT_ID}）`;

    const lan = await this.BUS_RPC.getLan({ app: remote_app });
    this.REMOTE_APP = remote_app;
    this.REMOTE_IP = lan.ip;
    this.REMOTE_PORT = lan.port;

    if (this.CLIENT) this.CLIENT.close();
    this.CLIENT = ClientProxyFactory.create({
      transport: Transport.TCP,
      options: { host: this.REMOTE_IP, port: this.REMOTE_PORT },
    });
    Logger.info({ log: `🚚已创建TCP链接：${this.CLIENT_NAME}` });
  }
}
